[ORG 0x500]
[SECTION .text]
[BITS 16]
jmp setup_start

SEG_BASE equ 0x0
SEG_LIMIT equ 0xfffff

KERNEL_ADDR equ 0x1200

CODE_SELECTOR equ 0x8
DATA_SELECTOR  equ 0x10

[SECTION .gdt]
gdt_start:
    dq 0x0
gdt_code:
    dw SEG_LIMIT & 0xffff
    dw SEG_BASE & 0xffff
    db (SEG_BASE >> 16) & 0xff
    ; 第一位1表示存在
    ; 第2、3段00，表示ring0
    ; 第4位1表示系统段
    ; 第5到8位1000，表示只执行
    ; 这里说的是从左到右的位，也就是先高位再低位
    db 0b1_00_1_1000
    ; 第一位0表示非G粒度, G=0 表示界限粒度为字节；
    ; 第二位D=1表示默认情况下指令使用32位地址及32位或8位操作数，这样的代码段也称为32位代码段；
    ; 第三四位0，表示AVL位，用于操作系统自己使用，CPU不会修改这个位；
    ; 第五到八位，表示段界限的高4位；
    db 0b0_1_00_0000 | ((SEG_LIMIT >> 16) & 0xf)
    ; 基址的高8位
    db ((SEG_BASE >> 24) & 0xff)

gdt_data:
    dw SEG_LIMIT & 0xffff
    dw SEG_BASE & 0xffff
    db (SEG_BASE >> 16) & 0xff
    ; 第一位1表示存在
    ; 第2、3段00，表示ring0
    ; 第4位1表示系统段
    ; 第5到8位0010，表示读写
    db 0b1_00_1_0010
    ; 第一位0表示非G粒度, G=0 表示界限粒度为字节；
    ; 第二位D=1表示段的上部界限为64K，这是为了与80286兼容。 
    ; 第三四位0，表示AVL位，用于操作系统自己使用，CPU不会修改这个位；
    ; 第五到八位，表示段界限的高4位；
    db 0b0_1_00_0000 | ((SEG_LIMIT >> 16) & 0xf)
    db ((SEG_BASE >> 24) & 0xff)

gdt_ptr:
    dw $ - gdt_start - 1
    dd gdt_start

[SECTION .text]
[BITS 16]
global setup_start
setup_start:
    mov ax, 0
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax

    mov si, prepare_enter_protected_mode_msg
    call print

enter_protected_mode:
    ; 关中断
    cli

    ; 加载gdt
    lgdt [gdt_ptr]

    ; 开A20地址线
    in al, 0x92
    or al, 0x2
    out 0x92, al

    ; 设置cr0寄存器的PE位
    mov eax, cr0
    or eax, 0x1
    mov cr0, eax

    ; 跳转到保护模式
    jmp CODE_SELECTOR:protected_mode

print:
    mov ah, 0x0e
    mov bh, 0
    mov bl, 1
.loop:
    mov al, [si]
    test al, al
    jz .done
    int 0x10
    inc si
    jmp .loop

.done:
    ret

[BITS 32]
protected_mode:
    mov ax, DATA_SELECTOR
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax

    mov esp, 0x9fbff

    mov edi, KERNEL_ADDR
    mov bl, 60
    mov ecx, 3
    call read_hd

    jmp CODE_SELECTOR:KERNEL_ADDR

read_hd:
    mov dx, 0x1f2
    mov al, bl
    out dx, al

    mov dx, 0x1f3
    mov al, cl
    out dx, al

    mov dx, 0x1f4
    mov al, ch
    out dx, al

    shr ecx, 16
    mov dx, 0x1f5
    mov al, cl
    out dx, al

    mov dx, 0x1f6
    and ch, 0x0f
    mov al, 0xe0
    or al, ch
    out dx, al

    mov dx, 0x1f7
    mov al, 0x20
    out dx, al

    xor cx, cx
    mov cl, bl
.start_read:
    push cx

    call wait_hd
    call read_hd_data

    pop cx
    loop .start_read
    ret

wait_hd:
    mov dx, 0x1f7
.check:
    in al, dx
    and al, 0x88
    cmp al, 0x08
    jnz .check
    ret

read_hd_data:
    mov dx, 0x1f0
    mov cx, 256
.read_word:
    in ax, dx
    mov [edi], ax
    add edi, 2
    loop .read_word
    ret

prepare_enter_protected_mode_msg db "prepare enter protected mode...", 0x0d, 0x0a, 0